import bpy
from typing import List, Union
from bpy.types import Object, Collection, bpy_prop_collection, ViewLayer, LayerCollection, Context
from ...addon.naming import FluidLabNaming
from .get_common_vars import get_common_vars


def get_view_layer_root_collection(context) -> Collection:
    return context.view_layer.layer_collection.collection


def set_active_collection(context: Context, coll: Collection) -> None:
    if coll:
        # root collection in view_layer:
        root_layerColl = context.view_layer.layer_collection
        to_coll = recurLayerCollection(context, root_layerColl, coll.name)
        bpy.data.scenes[context.scene.name].view_layers[context.view_layer.name].active_layer_collection = to_coll


def set_active_collection_by_name(context: Context, coll_name: str) -> None:
    if coll_name:
        # root collection in view_layer:
        root_layerColl = context.view_layer.layer_collection
        to_coll = recurLayerCollection(context, root_layerColl, coll_name)
        if to_coll:
            context.view_layer.active_layer_collection = to_coll


def create_new_collection(context: Context, new_collection_name: str, in_master_coll: bool = True) -> Collection:
    
    new_coll_target = None

    if in_master_coll:
    
        fluidlab = get_common_vars(context, get_fluidlab=True)
        set_active_collection_to_master_coll(context)

        if new_collection_name not in fluidlab.root_collection.children:
            new_coll_target = bpy.data.collections.new(new_collection_name)
            # print('create new collection')
            fluidlab.root_collection.children.link(new_coll_target)
    else:
        # print('new_collection_name', new_collection_name, 'context.collection', context.collection)
        if new_collection_name not in context.collection.children:
            new_coll_target = bpy.data.collections.new(new_collection_name)
            # print('create new collection')
            context.collection.children.link(new_coll_target)

    if new_coll_target:
        return new_coll_target


def move_objects_to_collection(context: Context, objects: list[Object], coll_name: str, with_print: bool = True) -> None:
    
    fluidlab = get_common_vars(context, get_fluidlab=True)
    
    print('move object to collection:', coll_name)

    # si no existe su coll target la creamos
    if coll_name not in fluidlab.root_collection.children:
        create_new_collection(context, coll_name)

    # para mmoverlos a su coleccion
    coll_target = fluidlab.root_collection.children.get(coll_name)

    if coll_target:

        moved = 0
        for obj in objects:

            # deslinko de donde esten
            if obj.name in coll_target.objects:
                if with_print:
                    print(obj.name + " It is already in the collection: " + coll_target.name)
                continue

            for coll in obj.users_collection:
                if coll.name != 'RigidBodyWorld':
                    coll.objects.unlink(obj)

            # los muevo a su coleccion
            coll_target.objects.link(obj)
            moved += 1

        fluidlab.chunks_selected = moved
    else:
        print('Not detected ' + coll_name + ' in fluidlab collection!')


def recurLayerCollection(context: Context, layerColl: Collection, collName: str):
    found = None

    if (layerColl.name == collName):
        return layerColl

    for layer in layerColl.children:
        found = recurLayerCollection(context, layer, collName)
        if found:
            return found


def set_active_collection_to_master_coll(context: Context) -> None:

    scn, fluidlab = get_common_vars(context, get_scn=True, get_fluidlab=True)

    if not fluidlab.root_collection:
        bpy.data.scenes[scn.name].view_layers[context.view_layer.name].active_layer_collection = scn.view_layers[context.view_layer.name].layer_collection
        fluidlab.root_collection = bpy.data.collections.new(FluidLabNaming.MAIN_COLL)
        scn.collection.children.link(fluidlab.root_collection)
        # Marcamos la collection como de tipo FluidLab_coll (para poder filtralas mejor):
        fluidlab.root_collection[FluidLabNaming.FluidLab] = True

    root_coll_name = fluidlab.root_collection.name

    root_layerColl = context.view_layer.layer_collection
    layer_collection = recurLayerCollection(context, root_layerColl, root_coll_name)

    if layer_collection:

        if root_coll_name in layer_collection.children:
            to_coll = layer_collection.children[root_coll_name]
        else:
            to_coll = layer_collection

        bpy.data.scenes[context.scene.name].view_layers[context.view_layer.name].active_layer_collection = to_coll
    else:
        print('No se encontro el layer collection de ' + root_coll_name)


def remove_collection(context: Context, coll: Collection, and_objects: bool = True) -> None:

    if isinstance(coll, Collection):

        if and_objects:

            if len(coll.objects) > 0:
                obs = [o for o in coll.objects if o.users == 1]
                while obs:
                    bpy.data.objects.remove(obs.pop())
        else:

            if len(coll.objects) > 0:
                for ob in coll.objects:
                    print(ob.name, ob.users_collection)
                    if len(ob.users_collection) <= 0:
                        context.scene.collection.objects.link(ob)
        
        bpy.data.collections.remove(coll)


def remove_collection_by_name(coll_name, and_objects: bool = True) -> None:

    coll = bpy.data.collections.get(coll_name)
    if coll:
        remove_collection(coll, and_objects)


def remove_collection_if_is_empty(context: Context, coll: Union[Collection,str]) -> None:

    if isinstance(coll, str):
    
        coll = bpy.data.collections.get(coll)
        if coll:
            remove_collection_if_is_empty(context, coll)

    elif isinstance(coll, Collection):
    
        if len(coll.objects) == 0 and len(coll.children_recursive) == 0:
            remove_collection(context, coll, False)


def move_collection_from_to_collection(target_coll: Collection, coll_dest: Collection) -> None:

    def traverse_tree(t):
        yield t
        for child in t.children:
            yield from traverse_tree(child)

    def parent_lookup(coll):
        parent_lookup = {}
        for coll in traverse_tree(coll):
            for c in coll.children.keys():
                parent_lookup.setdefault(c, coll)
        return parent_lookup

    coll_scene = bpy.data.collections[FluidLabNaming.MAIN_COLL]
    coll_parents = parent_lookup(coll_scene)
    coll_parent = coll_parents.get(target_coll.name)

    if coll_parent:
        # Unlink *active_coll*
        coll_parent.children.unlink(target_coll)

        # Link *active_coll* to *target_coll*
        coll_dest.children.link(target_coll)


def find_layer_coll_by_coll_name(data, container: List, coll_name: str):

    # print('data:', data, 'data type:', type(data), 'container:', container, 'coll_name:', coll_name)

    if isinstance(data, bpy_prop_collection):
        for vl in data:
            if hasattr(vl, 'layer_collection'):
                if len(vl.layer_collection.children) > 0:
                    lc = vl.layer_collection
                    for child in lc.children:

                        if child.name == coll_name:
                            container.append(lc)
                        else:
                            find_layer_coll_by_coll_name(child, container, coll_name)

    elif isinstance(data, ViewLayer):
        for vl in data:
            lc = vl.layer_collection
            if len(lc.children) > 0:
                for child in lc.children:

                    if child.name == coll_name:
                        container.append(lc)
                    else:
                        find_layer_coll_by_coll_name(child, container, coll_name)

    elif isinstance(data, LayerCollection):
        if len(data.children) > 0:
            for lc in data.children:

                if lc.name == coll_name:
                    container.append(lc)
                else:
                    find_layer_coll_by_coll_name(lc, container, coll_name)

    if container:
        return container[0]


def hide_unhide_collection(context: Context, coll_name: str, mode: str, visibility: bool) -> None:

    """ Example of use: hide_unhide_collection(context, coll.name, 'hide_viewport', visibility=self.visible) """

    if coll_name in bpy.data.collections:

        data = context.scene.view_layers
        container = []
        layer_collection = find_layer_coll_by_coll_name(data, container, coll_name)
        if layer_collection:
            previous_layer_collection = context.view_layer.active_layer_collection
            context.view_layer.active_layer_collection = layer_collection
            setattr(context.view_layer.active_layer_collection, mode, visibility)
            context.view_layer.active_layer_collection = previous_layer_collection
